"""
MCP客户端服务

提供MCP客户端管理的业务逻辑层
"""

from typing import Dict, Any, Optional, List
from datetime import datetime, timedelta
from sqlalchemy.orm import Session
import uuid
import re

from app.models.mcp_session import MCPSession, SessionStatus
from app.models.mcp_message import MCPMessage, MessageType, MessageDirection, MessageStatus
from app.core.mcp.client import MCPClient
from app.core.mcp.exceptions import MCPException
from app.core.config import settings
from app.core.logging_config import app_logger
from app.services.mcp_context_service import MCPContextService


class MCPClientService:
    """MCP客户端服务类"""
    
    # 存储活跃的MCP客户端实例
    _active_clients: Dict[str, MCPClient] = {}
    
    def __init__(self, db: Session, user_id: int, redis_client: Optional[Any] = None):
        """
        初始化MCP客户端服务
        
        Args:
            db: 数据库会话
            user_id: 用户ID
            redis_client: Redis客户端（可选）
        """
        self.db = db
        self.user_id = user_id
        self.redis = redis_client
        # 暂时注释掉context_service的初始化，避免可能的导入问题
        # self.context_service = MCPContextService(db=db, redis_client=redis_client)
        self.logger = app_logger
    
    async def create_session(
        self,
        mcp_server_url: Optional[str] = None,
        conversation_id: Optional[int] = None,
        model_config_id: Optional[int] = None,
        initial_context: Optional[Dict[str, Any]] = None,
        connection_config: Optional[Dict[str, Any]] = None
    ) -> MCPSession:
        """
        创建MCP会话
        
        Args:
            mcp_server_url: MCP服务器地址（可选，使用默认配置）
            conversation_id: 关联的对话ID
            model_config_id: 关联的模型配置ID
            initial_context: 初始上下文数据
            connection_config: 连接配置
            
        Returns:
            MCPSession: 创建的会话对象
        """
        # 生成会话ID
        session_id = str(uuid.uuid4())
        
        # 使用默认MCP服务器地址
        server_url = mcp_server_url or settings.mcp_server_url
        
        # 验证服务器URL格式
        if not self._is_valid_websocket_url(server_url):
            raise ValueError(f"无效的WebSocket URL: {server_url}。请使用 ws:// 或 wss:// 开头的地址")
        
        # 创建会话记录
        session = MCPSession(
            user_id=self.user_id,
            session_id=session_id,
            mcp_server_url=server_url,
            conversation_id=conversation_id,
            model_config_id=model_config_id,
            status=SessionStatus.CONNECTING,
            is_active=True,
            connection_config=connection_config or {},
            message_count=0,
            error_count=0
        )
        
        self.db.add(session)
        self.db.commit()
        self.db.refresh(session)
        
        try:
            # 创建MCP客户端
            client = MCPClient(
                server_url=server_url,
                session_id=session_id,
                timeout=settings.mcp_connection_timeout,
                max_retries=settings.mcp_max_retries,
                heartbeat_interval=settings.mcp_heartbeat_interval,
                on_connect=self._on_connect_callback,
                on_disconnect=self._on_disconnect_callback,
                on_error=self._on_error_callback
            )
            
            # 建立连接
            await client.connect()
            
            # 存储客户端实例
            self._active_clients[session_id] = client
            
            # 更新会话状态
            session.status = SessionStatus.CONNECTED
            session.connected_at = datetime.utcnow()
            self.db.commit()
            self.db.refresh(session)
            
            # 如果有初始上下文，创建上下文记录
            # 暂时注释掉context_service相关功能
            # if initial_context:
            #     await self.context_service.create_context(
            #         session_id=session_id,
            #         context_data=initial_context,
            #         context_type="system"
            #     )
            
            self.logger.info(f"MCP会话创建成功: session_id={session_id}, user_id={self.user_id}")
            
            return session
            
        except Exception as e:
            # 更新会话状态为错误
            session.status = SessionStatus.ERROR
            session.error_count += 1
            self.db.commit()
            
            self.logger.error(f"MCP会话创建失败: {str(e)}", exc_info=e)
            raise
    
    async def get_session(self, session_id: str) -> Optional[MCPSession]:
        """获取会话"""
        session = self.db.query(MCPSession).filter(
            MCPSession.session_id == session_id
        ).first()
        return session
    
    async def get_user_sessions(
        self,
        is_active: Optional[bool] = None,
        limit: int = 50
    ) -> List[MCPSession]:
        """
        获取用户的会话列表
        
        Args:
            is_active: 是否只返回活跃会话
            limit: 返回数量限制
            
        Returns:
            List[MCPSession]: 会话列表
        """
        query = self.db.query(MCPSession).filter(
            MCPSession.user_id == self.user_id
        )
        
        if is_active is not None:
            query = query.filter(MCPSession.is_active == is_active)
        
        sessions = query.order_by(MCPSession.created_at.desc()).limit(limit).all()
        
        return sessions
    
    async def send_request(
        self,
        session_id: str,
        payload: Dict[str, Any],
        update_context: bool = True,
        timeout: Optional[int] = None
    ) -> Dict[str, Any]:
        """
        发送MCP请求
        
        Args:
            session_id: 会话ID
            payload: 请求负载数据
            update_context: 是否更新上下文
            timeout: 超时时间
            
        Returns:
            Dict[str, Any]: 响应数据
        """
        # 获取客户端
        client = self._active_clients.get(session_id)
        if not client:
            raise ValueError(f"会话不存在或未连接: session_id={session_id}")
        
        # 获取会话记录
        session = await self.get_session(session_id)
        if not session:
            raise ValueError(f"会话记录不存在: session_id={session_id}")
        
        # 记录开始时间
        start_time = datetime.utcnow()
        message_id = str(uuid.uuid4())
        
        # 创建消息记录（发送）
        send_message = MCPMessage(
            session_id=session_id,
            message_id=message_id,
            message_type=MessageType.REQUEST,
            direction=MessageDirection.SEND,
            payload=payload,
            status=MessageStatus.PENDING,
            send_at=start_time
        )
        self.db.add(send_message)
        self.db.commit()
        
        try:
            # 发送请求
            response_payload = await client.send_request(payload=payload, timeout=timeout)
            
            # 计算处理时长
            end_time = datetime.utcnow()
            duration_ms = int((end_time - start_time).total_seconds() * 1000)
            
            # 更新发送消息状态
            send_message.status = MessageStatus.SENT
            send_message.duration_ms = duration_ms
            
            # 创建响应消息记录
            receive_message = MCPMessage(
                session_id=session_id,
                message_id=message_id,  # 使用相同的message_id关联
                message_type=MessageType.RESPONSE,
                direction=MessageDirection.RECEIVE,
                payload=response_payload,
                status=MessageStatus.RECEIVED,
                receive_at=end_time,
                duration_ms=duration_ms
            )
            self.db.add(receive_message)
            
            # 更新会话统计
            session.message_count += 2  # 请求和响应各计一条
            session.last_heartbeat_at = end_time
            
            self.db.commit()
            
            # 如果需要更新上下文
            # 暂时注释掉context_service相关功能
            # if update_context and response_payload:
            #     await self.context_service.create_context(
            #         session_id=session_id,
            #         context_data=response_payload,
            #         context_type="assistant"
            #     )
            
            self.logger.info(f"MCP请求成功: session_id={session_id}, duration={duration_ms}ms")
            
            return {
                "response": response_payload,
                "context_updated": update_context,
                "message_id": message_id,
                "duration_ms": duration_ms
            }
            
        except Exception as e:
            # 更新消息状态为失败
            send_message.status = MessageStatus.FAILED
            send_message.error_message = str(e)
            
            # 更新会话错误计数
            session.error_count += 1
            
            self.db.commit()
            
            self.logger.error(f"MCP请求失败: {str(e)}", exc_info=e)
            raise
    
    async def delete_session(self, session_id: str) -> bool:
        """
        删除会话
        
        Args:
            session_id: 会话ID
            
        Returns:
            bool: 是否成功删除
        """
        # 获取会话
        session = await self.get_session(session_id)
        if not session:
            return False
        
        # 如果有活跃客户端，先断开连接
        client = self._active_clients.get(session_id)
        if client:
            try:
                await client.disconnect()
            except Exception as e:
                self.logger.error(f"断开MCP连接失败: {str(e)}")
            
            # 移除客户端实例
            del self._active_clients[session_id]
        
        # 更新会话状态
        session.is_active = False
        session.status = SessionStatus.DISCONNECTED
        session.disconnected_at = datetime.utcnow()
        
        self.db.commit()
        
        self.logger.info(f"MCP会话已删除: session_id={session_id}")
        
        return True
    
    async def _on_connect_callback(self, session_id: str) -> None:
        """连接建立回调"""
        self.logger.info(f"MCP连接建立回调: session_id={session_id}")
        
        # 更新会话的最后心跳时间
        session = await self.get_session(session_id)
        if session:
            session.last_heartbeat_at = datetime.utcnow()
            self.db.commit()
    
    async def _on_disconnect_callback(self, session_id: str) -> None:
        """断开连接回调"""
        self.logger.info(f"MCP断开连接回调: session_id={session_id}")
        
        # 更新会话状态
        session = await self.get_session(session_id)
        if session:
            session.status = SessionStatus.DISCONNECTED
            session.disconnected_at = datetime.utcnow()
            self.db.commit()
    
    async def _on_error_callback(self, error_code: str, error_message: str) -> None:
        """错误回调"""
        self.logger.error(f"MCP错误回调: code={error_code}, message={error_message}")
    
    @classmethod
    async def cleanup_inactive_sessions(cls, db: Session, hours: int = 24) -> int:
        """
        清理不活跃的会话
        
        Args:
            db: 数据库会话
            hours: 不活跃时间阈值（小时）
            
        Returns:
            int: 清理的会话数量
        """
        threshold = datetime.utcnow() - timedelta(hours=hours)
        
        # 查询不活跃的会话
        inactive_sessions = db.query(MCPSession).filter(
            MCPSession.is_active == True,
            MCPSession.last_heartbeat_at < threshold
        ).all()
        
        count = 0
        for session in inactive_sessions:
            # 如果有活跃客户端，断开连接
            if session.session_id in cls._active_clients:
                try:
                    client = cls._active_clients[session.session_id]
                    await client.disconnect()
                    del cls._active_clients[session.session_id]
                except Exception as e:
                    app_logger.error(f"清理会话失败: {str(e)}")
            
            # 更新状态
            session.is_active = False
            session.status = SessionStatus.DISCONNECTED
            session.disconnected_at = datetime.utcnow()
            count += 1
        
        db.commit()
        
        app_logger.info(f"清理了{count}个不活跃的MCP会话")
        
        return count
    
    def _is_valid_websocket_url(self, url: str) -> bool:
        """
        验证WebSocket URL格式
        
        Args:
            url: 要验证的URL
            
        Returns:
            bool: 是否为有效的WebSocket URL
        """
        if not url or not isinstance(url, str):
            return False
        
        # WebSocket URL必须以 ws:// 或 wss:// 开头
        if not url.startswith(('ws://', 'wss://')):
            return False
        
        # 基本URL格式验证
        try:
            # 简单的URL格式检查
            pattern = r'^(ws|wss)://[a-zA-Z0-9.-]+(:\d+)?(/.*)?$'
            return bool(re.match(pattern, url))
        except Exception:
            return False
    
    def get_user_sessions_count(self, is_active: Optional[bool] = None) -> int:
        """
        获取用户的会话数量
        
        Args:
            is_active: 是否只统计活跃会话
            
        Returns:
            int: 会话数量
        """
        query = self.db.query(MCPSession).filter(
            MCPSession.user_id == self.user_id
        )
        
        if is_active is not None:
            query = query.filter(MCPSession.is_active == is_active)
        
        return query.count()
    
    def get_user_messages_count(self) -> int:
        """
        获取用户的消息总数
        
        Returns:
            int: 消息总数
        """
        # 通过会话关联查询消息
        return self.db.query(MCPMessage).join(MCPSession).filter(
            MCPSession.user_id == self.user_id
        ).count()
    
    def get_user_errors_count(self) -> int:
        """
        获取用户的错误消息数量
        
        Returns:
            int: 错误消息数量
        """
        # 查询状态为ERROR的消息
        return self.db.query(MCPMessage).join(MCPSession).filter(
            MCPSession.user_id == self.user_id,
            MCPMessage.status == MessageStatus.ERROR
        ).count()

